/**
 * @file   ModSyncUpdate.java
 * @author Omar Quintana <o.quintana@lpcsite.com> y Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Thu Aug 30 12:39:37 2007
 *
 * @brief  Actualización archivos desde el servidor.
 *
 *
 */

package sim.sync;

import sim.auth.DocAuth;
import sim.framework.*;
import sim.framework.io.*;
import sim.framework.dialog.*;
import sim.store.*;
import sim.utils.*;


public final class ModSyncUpdate extends ModSimio {
    
    private static final byte[] CMD_REQUEST_LIST = {'s', 'y', 'n', 'c'};
    
    private static final int STAGE_DOWNLOAD = 1; // Descarga lista de items a sincronizar
    private static final int STAGE_UPDATING = 2; // Descargando items de la lista.
    
    private int m_Stage = STAGE_DOWNLOAD;
    private SyncItem m_CurrItem = null;
    private Object m_Object;
    private DocAuth m_DocAuth = null;
    private DocSync m_DocSync = null;

    protected Buffer m_BufInp; // Cache de datos leidos.
    protected Buffer m_BufOut; // Cadhe de datos enviados.

    // Variables utilizadas por StoreTxt
    private StoreTxtId m_RecordId = new StoreTxtId();
    private DataTxt m_DataTxt = null;
    
    public ModSyncUpdate(int i, Simlet s) {
        super(i, s);
	// Registro par actualización por campo
	m_DataTxt = new DataTxt(Utils.TOKEN, Utils.TOKEN_PIPE, 1,
                                16, m_Simlet.getAppEncoding());
	// Documentos utiles.
        m_DocSync = (DocSync)getDocument(Simlet.MOD_SYNC);
        m_DocAuth = (DocAuth)getDocument(Simlet.MOD_AUTH);
    }
    
    protected int simioClient(){
        int ret = ModSimio.CANCEL;

	switch(m_Stage) {
	case ModSyncUpdate.STAGE_DOWNLOAD:
	    ret = clientList();
	    break;
	case ModSyncUpdate.STAGE_UPDATING:
	    ret = clientItem();
	}

        return ret;
    }

    public void work(final Msg msg) {

	if ((m_DocAuth != null) && (m_DocSync != null)) {
	    super.work(msg);
	}
	else {
	    execBack();
	}
    }

    private int clientList() {

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::clientList() ENTERED");
	//#endif

        switch (getStatus()) {
	case ModSimio.STATUS_COMMAND:
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tEscribiendo comando ...");
	    //#endif
	    // Comando de sincronización
	    write(CMD_REQUEST_LIST);
	    // Espacio en blanco
	    write(Utils.TOKEN, Utils.TOKEN_SPACE, 1);
	    // Login del usuario
	    write(m_DocAuth.getLogin());
	    // Espacio en blanco
	    write(Utils.TOKEN, Utils.TOKEN_SPACE, 1);
	    // Tiempo de última sincronización
	    write(m_DocSync.getTimeStamp());
	    // Espacio en blanco
	    write(Utils.TOKEN, Utils.TOKEN_SPACE, 1);
	    // ID del equipo
	    write(m_DocSync.getTimeStamp());
	    // Fin de linea
	    write(Utils.ENDOFLINE, 0, Utils.ENDOFLINE.length);
	    // Hey mono, envía el comando.
	    break;
	case ModSimio.STATUS_INPUT:
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tgetStatus() = ModSimio.INPUT");
	    //#endif
	    // Leer linea por linea, esta función llama a simioGetsLine().
	    gets();
	    break;
	case ModSimio.STATUS_READY:
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tgetStatus() = ModSimio.READY");
	    //#endif
	    // Establecer el estado de envío de items.
	    m_Stage = ModSyncUpdate.STAGE_UPDATING;
	    // Solicitar el envío de items.
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tSolicita enviar comando");
	    //#endif
	    sendCommand();
	    break;
        }

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::clientList() EXITING");
	//#endif

        return ModSimio.OK;
    }

    private int clientItem() {
	int ret;

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::clientItem() ENTERED");
	//#endif

	ret = ModSimio.OK;

        switch (getStatus()) {
	case ModSimio.STATUS_COMMAND:
	    m_CurrItem = m_DocSync.removeItem();
	    if (m_CurrItem != null) {
		//#ifdef DEBUG_MODSYNCUPDATE
		//# System.out.println("\tAbriendo objeto ...");
		//#endif
		ret = ModSimio.CANCEL;
		// Abrimos el objeto
		if (openObject()) {
		    //#ifdef DEBUG_MODSYNCUPDATE
		    //# System.out.println("\tObjeto abierto, escribiendo comando ...");
		    //#endif
		    // Comando
		    write(m_CurrItem.m_Name);
		    // espacio
		    write(Utils.TOKEN, Utils.TOKEN_SPACE, 1);
		    // Modo
		    write(SyncItem.MODES, m_CurrItem.m_Mode, 1);
		    // espacio
		    write(Utils.TOKEN, Utils.TOKEN_SPACE, 1);
		    // Login del usuario
		    write(m_DocAuth.getLogin());
		    // espacio
		    write(Utils.TOKEN, Utils.TOKEN_SPACE, 1);
		    // Tiempo de última sincronización
		    write(m_DocSync.getTimeStamp());
		    // Fin de línea
		    write(Utils.ENDOFLINE);
		    // Hey mono, envía el comando.
		    ret = ModSimio.OK;
		}
	    }
	    else {
		//#ifdef DEBUG_MODSYNCUPDATE
		//# System.out.println("\tNo hay más objetos, nos vamos por delante NEXT ...");
		//#endif
		closeObject();
		execNext();
	    }
	    break;
	default:
	    switch (m_CurrItem.m_Type) {
	    case SyncItem.TYPE_TXT:
		ret = updateItemTxt();
		break;
	    case SyncItem.TYPE_BIN:
		ret = updateItemBin();
		break;
	    case SyncItem.TYPE_TBL:
		ret = updateItemTbl();
		break;
	    }

	}

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::clientItem() EXITING");
	//#endif

	return ret;

    }

    private int updateItemTxt() {
	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::updateItemTxt() ENTERED");
	//#endif
	//	file = (FileStream)m_Object;
        switch (getStatus()) {
	case ModSimio.STATUS_INPUT:
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tgetStatus() = ModSimio.INPUT");
	    //#endif
	    read();
	    break;
	case ModSimio.STATUS_READY:
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tgetStatus() = ModSimio.READY");
	    //#endif
	    closeObject();
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tSolicita enviar comando");
	    //#endif
	    sendCommand();
	    break;
        }

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::updateItemTxt() EXITING");
	//#endif

        return ModSimio.OK;
    }
    
    private int updateItemBin() {
	return updateItemTxt();
    }

    private int updateItemTbl() {

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::updateItemTbl() ENTERED");
	//#endif       

        switch (getStatus()) {
	case ModSimio.STATUS_INPUT:
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tgetStatus() = ModSimio.INPUT");
	    //#endif
	    // Leer linea por linea, esta función llama a simioGetsLine().
	    gets();
	    break;
	case ModSimio.STATUS_READY:
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tgetStatus() = ModSimio.READY");
	    //#endif
	    closeObject();
	    //#ifdef DEBUG_MODSYNCUPDATE
	    //# System.out.println("\tSolicita enviar comando");
	    //#endif
	    sendCommand();
	    break;
        }

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::updateItemTbl() EXITING");
	//#endif

        return ModSimio.OK;
    }
    
    private boolean openObject() {
	closeObject();
        if (m_CurrItem != null) {
            switch (m_CurrItem.m_Type) {
	    case SyncItem.TYPE_TBL:
		// Si el modo de actulización es remake ...
		if (m_CurrItem.m_Mode == SyncItem.MODE_REMAKE) {
		    // ... se debe re hacer la tabla.
		    File.delete(new String(m_CurrItem.m_Name, 0, m_CurrItem.m_Name.length));
		}
		// Crear un tabla.
		m_Object = StoreTxt.create(m_Simlet, m_CurrItem.getName());
		break;
	    case SyncItem.TYPE_BIN:
	    case SyncItem.TYPE_TXT:
		// Crear un Archivo.
		m_Object = FileStream.create(m_Simlet, m_CurrItem.getName(), File.MODE_WRITE);
            }
        }
        return (m_Object != null);
    }

    private void closeObject() {
	FileStream file;
	StoreTxt table;
	if ((m_CurrItem != null) && (m_Object != null)) {
	    switch (m_CurrItem.m_Type) {
	    case SyncItem.TYPE_TBL:
		table = (StoreTxt)m_Object;
		table.close();
		break;
	    case SyncItem.TYPE_TXT:
	    case SyncItem.TYPE_BIN:
		file = (FileStream)m_Object;
		file.close();
		break;
	    }
	    m_Object = null;
	}
    }
                    
    protected boolean simioGetsLine(final byte[] line, int start, int length) {

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("\t" + new String(line, start, length));
	//#endif

        boolean ret = false;
        switch (m_Stage) {
	case STAGE_DOWNLOAD:
	    ret = getLineToDownloadList(line, start, length);
	    break;
	case STAGE_UPDATING:
	    switch(m_CurrItem.m_Type) {
	    case SyncItem.TYPE_TBL:
		ret = getLineToUpdateTable(line, start, length);
		break;
	    }
        }

        return ret;
    }

    protected int simioReadData(final byte[] line, int start, int length) {
	int ret = 0;
        switch (m_Stage) {
	case STAGE_UPDATING:
	    switch(m_CurrItem.m_Type) {
	    case SyncItem.TYPE_BIN:
		// Hacer la conversion de Code Base 64 a binario.
		// Proxima version ...
		// ret = readDataToDownloadCodBase64(line, start, length);
	    case SyncItem.TYPE_TXT:
		ret = readDataToDownloadFile(line, start, length);
		break;
	    }
        }
        return ret;

    }
    
    private boolean getLineToDownloadList(final byte[] line, int start, int length) {
        SyncItem item;
        item = SyncItem.create(line, start, length);
        if (item != null) {
            m_DocSync.appendItem(item);
        }
        return true;
    }

    private boolean getLineToUpdateTable(final byte[] line, int start, int length) {
        // se define aquí para no hacer new en cada iteración
        switch (m_CurrItem.m_Mode) {
	case SyncItem.MODE_APPEND:
	case SyncItem.MODE_REMAKE:
	    // Insertar nuevos registros.
	    updateItemTblInsert(line, start, length);
	    break;
	case SyncItem.MODE_UPDATE:
	    // Actualizar registros.
	    updateItemTblUpdate(line, start, length);
	    break;
	case SyncItem.MODE_DELETE:
	    // Borrar registros.
	    updateItemTblDelete(line, start, length);
	    break;
	case SyncItem.MODE_FIELDS:
	    // Actualiza campos.
	    updateItemTblFields(line, start, length);
	    break;
        }
        return true;
    }
    
    private void updateItemTblInsert(byte[] line, int start, int length) {
        StoreTxt table;
        table = (StoreTxt)m_Object;
        table.insert(m_RecordId, line, start, length);
    }
    
    private void updateItemTblUpdate(byte[] line, int start, int length) {
        StoreTxt table;
        int tokPos;
        table = (StoreTxt)m_Object;
        // se verifica si existe un primer registro como identificador
        tokPos = Utils.memmem(line, start, length, Utils.TOKEN, Utils.TOKEN_PIPE, 1);
        if (tokPos > 0) {
            if (table.search(m_RecordId, 0, line, start, tokPos - start, true)) {
                table.update(m_RecordId, line, start, length);
            } else {
                table.insert(m_RecordId, line, start, length);
            }
        }
    }
    
    private void updateItemTblDelete(byte[] line, int start, int length) {
        StoreTxt table;
        table = (StoreTxt)m_Object;
        if (table.search(m_RecordId, 0, line, start, length, true)) {
            table.delete(m_RecordId);
        }
    }
    
    private void updateItemTblFields(byte[] line, int start, int length) {
        StoreTxt table;
        String value;   // almacena el valor que se debe actualizar
        int tokPos;     // posición donde termina el identificador a actualizar
        table = (StoreTxt)m_Object;
        // Si se encuentra la llave primaria ...
        tokPos = Utils.memmem(line, start, length, Utils.TOKEN, Utils.TOKEN_PIPE, 1);
        if (tokPos > start) {
	    // ... y se encuentra el registro ...
            if (table.search(m_RecordId, 0, line, start, (tokPos - start), true)) {
                // ... cargar los datos de la tabla ...
                table.load(m_RecordId, m_DataTxt);
                // ... establece el valor del campo ...
		m_DataTxt.set(m_CurrItem.m_Field, line, tokPos + 1, length - (tokPos - start + 1));
		// ... actualizar la tabla.
                table.save(m_DataTxt);
            }
        }
    }

    private int readDataToDownloadFile(final byte[] line, int start, int length) {
        FileStream file;
        file = (FileStream)m_Object;
        return file.write(line, start, length);
    }
    
    protected int simioCancel() {

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::simioCancel() ENTERED");
	//#endif

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("\tNo hay más objetos, nos vamos por detrás BACK ...");
	//#endif
	closeObject();
	execBack();

	//#ifdef DEBUG_MODSYNCUPDATE
	//# System.out.println("ModSyncUpdate::simioCancel() EXITING");
	//#endif

        return 0;
    }    
}

